﻿namespace NTLib.Core.UTest.Reflection
{
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using NTLib.Core.Attributes;
    using NTLib.Core.Exceptions;
    using NTLib.Core.Reflection;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Threading.Tasks;

    [TestClass]
    public class ReflectionUTest
    {
        #region Fields

        public const string CATEGORY = "Core.Reflection";
        
        #endregion

        #region Samples

        private interface I
        {

        }

        [TestClass]
        private class A
        {
            public T Create<T>()
                where T : new()
            {
                return new T();
            }
        }

        private class B : A
        {

        }

        [TestClass]
        private class C : A
        {

        }

        private class D : I
        {

        }

        [Guid(GUID)]
        private class IdClass
        {
            public const string GUID = "ACD0A6CC-E0EF-44DC-8A27-BB8E550138A7";
        }

        #endregion

        #region Methods

        /// <summary>
        /// Reflection_s the is or inherit.
        /// </summary>
        [TestCategory(CATEGORY)]
        [TestMethod]
        public void Reflection_IsOrInherit()
        {
            A a = new A();
            B b = new B();
            C c = new C();
            D d = new D();

            Assert.IsTrue(a.IsOrInheritOf(typeof(A)));
            Assert.IsTrue(a.GetType().IsOrInheritOf(typeof(A)));
            Assert.IsTrue(a.GetType().GetTypeInfo().IsOrInheritOf(typeof(A)));
            Assert.IsTrue(a.GetType().GetTypeInfo().IsOrInheritOf(typeof(A).GetTypeInfo()));

            Assert.IsTrue(b.IsOrInheritOf(typeof(A)));
            Assert.IsTrue(b.GetType().IsOrInheritOf(typeof(A)));
            Assert.IsTrue(b.GetType().GetTypeInfo().IsOrInheritOf(typeof(A)));
            Assert.IsTrue(b.GetType().GetTypeInfo().IsOrInheritOf(typeof(A).GetTypeInfo()));

            Assert.IsTrue(c.IsOrInheritOf(typeof(A)));
            Assert.IsTrue(c.GetType().IsOrInheritOf(typeof(A)));
            Assert.IsTrue(c.GetType().GetTypeInfo().IsOrInheritOf(typeof(A)));
            Assert.IsTrue(c.GetType().GetTypeInfo().IsOrInheritOf(typeof(A).GetTypeInfo()));

            Assert.IsTrue(d.IsOrInheritOf(typeof(I)));
            Assert.IsTrue(d.GetType().IsOrInheritOf(typeof(I)));
            Assert.IsTrue(d.GetType().GetTypeInfo().IsOrInheritOf(typeof(I)));
            Assert.IsTrue(d.GetType().GetTypeInfo().IsOrInheritOf(typeof(I).GetTypeInfo()));

            Assert.IsFalse(d.IsOrInheritOf(typeof(A)));
            Assert.IsFalse(d.GetType().IsOrInheritOf(typeof(A)));
            Assert.IsFalse(d.GetType().GetTypeInfo().IsOrInheritOf(typeof(A)));
            Assert.IsFalse(d.GetType().GetTypeInfo().IsOrInheritOf(typeof(A).GetTypeInfo()));

            Assert.IsFalse(b.IsOrInheritOf(typeof(C)));
            Assert.IsFalse(b.GetType().IsOrInheritOf(typeof(C)));
            Assert.IsFalse(b.GetType().GetTypeInfo().IsOrInheritOf(typeof(C)));
            Assert.IsFalse(b.GetType().GetTypeInfo().IsOrInheritOf(typeof(C).GetTypeInfo()));

            Assert.IsFalse(a.IsOrInheritOf(typeof(C)));
            Assert.IsFalse(a.GetType().IsOrInheritOf(typeof(C)));
            Assert.IsFalse(a.GetType().GetTypeInfo().IsOrInheritOf(typeof(C)));
            Assert.IsFalse(a.GetType().GetTypeInfo().IsOrInheritOf(typeof(C).GetTypeInfo()));
        }

        [TestCategory(CATEGORY)]
        [TestMethod]
        public void Reflection_AsAttr()
        {
            A a = new A();
            B b = new B();
            C c = new C();
            D d = new D();

            Assert.IsTrue(typeof(A).HasAttr<TestClassAttribute>());
            Assert.IsTrue(typeof(C).HasAttr<TestClassAttribute>());
            Assert.IsTrue(typeof(B).HasAttr<TestClassAttribute>(true));

            Assert.IsFalse(typeof(B).HasAttr<TestClassAttribute>());
            Assert.IsFalse(typeof(D).HasAttr<TestClassAttribute>());
            Assert.IsFalse(typeof(D).HasAttr<TestClassAttribute>(true));

            Assert.IsTrue(a.HasAttr<TestClassAttribute>());
            Assert.IsTrue(c.HasAttr<TestClassAttribute>());
            Assert.IsTrue(b.HasAttr<TestClassAttribute>(true));

            Assert.IsFalse(b.HasAttr<TestClassAttribute>());
            Assert.IsFalse(d.HasAttr<TestClassAttribute>());
            Assert.IsFalse(d.HasAttr<TestClassAttribute>(true));
        }

        [TestCategory(CATEGORY)]
        [TestMethod]
        public void Reflection_GetMandatoryAttribute()
        {
            A a = new A();
            B b = new B();
            C c = new C();
            D d = new D();

            Assert.IsNotNull(typeof(A).GetMandatoryAttribute<TestClassAttribute>());
            Assert.IsNotNull(typeof(C).GetMandatoryAttribute<TestClassAttribute>());
            Assert.IsNotNull(typeof(B).GetMandatoryAttribute<TestClassAttribute>(true));

            try
            {

                typeof(B).GetMandatoryAttribute<TestClassAttribute>();
            }
            catch (AttributeMandatoryException aEx)
            {
                Assert.IsNotNull(aEx.OriginType);
                Assert.IsNotNull(aEx.MissingAttributeType);
                Assert.AreNotEqual(aEx.MissingAttributeType, aEx.OriginType);
            }
            try
            {

                typeof(D).GetMandatoryAttribute<TestClassAttribute>();
            }
            catch (AttributeMandatoryException dEx)
            {
                Assert.IsNotNull(dEx.OriginType);
                Assert.IsNotNull(dEx.MissingAttributeType);
                Assert.AreNotEqual(dEx.MissingAttributeType, dEx.OriginType);
            }

            try
            {
                typeof(D).GetMandatoryAttribute<TestClassAttribute>(true);
            }
            catch (AttributeMandatoryException)
            {
            }
        }

        [TestCategory(CATEGORY)]
        [TestMethod]
        public void Reflection_GetDeclaredMethod()
        {
            MethodInfo info = typeof(object).GetTypeInfo().GetDeclaredMethod("ToString");
            var infoLambda = TypeExtensions.GetDeclaredMethod<A>(a => a.ToString());

            Assert.AreEqual(info, infoLambda);

            MethodInfo genericOne = typeof(A).GetTypeInfo().GetDeclaredMethod("Create");
            var genericOneLambda = TypeExtensions.GetDeclaredMethod<A>(a => a.Create<object>());

            Assert.AreEqual(genericOne, genericOneLambda);
        }

        [TestCategory(CATEGORY)]
        [TestMethod]
        public void Reflection_GetTypeId()
        {
            Assert.AreEqual(typeof(IdClass).GetId().ToString(), IdClass.GUID, true);
        }

        [TestCategory(CATEGORY)]
        [TestMethod]
        [ExpectedException(typeof(AttributeMandatoryException))]
        public void Reflection_GetTypeId_NOAttr()
        {
            typeof(A).GetId().ToString();
        }

        #endregion
    }
}
